//! Example demonstrating polymorphic usage of the GridGraph trait
//!
//! This example shows how the GridGraph trait allows you to write generic
//! functions that work with any grid graph type (2D 4-connected, 8-connected,
//! or multi-threaded variants).

use grid_cut_rs::{GridGraph, GridGraph2D4C, GridGraph2D8C, GridGraph2D4CMT};

/// Generic function that works with any GridGraph implementation
fn compute_and_analyze<G>(graph: &mut G, name: &str)
where
    G: GridGraph,
    G::Flow: std::fmt::Display,
{
    println!("\n=== {} ===", name);
    
    // Compute maximum flow
    graph.compute_maxflow();
    
    // Get the flow value
    let flow = graph.get_flow();
    println!("Maximum flow: {}", flow);
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("GridGraph Trait Polymorphism Example");
    println!("=====================================");
    
    // Create a simple 5x5 grid problem
    let width = 5;
    let height = 5;
    
    // Example 1: GridGraph2D4C (4-connected)
    {
        let mut graph = GridGraph2D4C::<i32, i32, i32>::new(width, height)?;
        
        // Set up a simple segmentation problem
        // Source in top-left corner
        let source = graph.node_id(0, 0)?;
        graph.set_terminal_cap(source, 100, 0)?;
        
        // Sink in bottom-right corner
        let sink = graph.node_id(width - 1, height - 1)?;
        graph.set_terminal_cap(sink, 0, 100)?;
        
        // Add some neighbor capacities
        for y in 0..height {
            for x in 0..width {
                let node = graph.node_id(x, y)?;
                
                // Connect to right neighbor
                if x < width - 1 {
                    graph.set_neighbor_cap(node, 1, 0, 10)?;
                }
                
                // Connect to bottom neighbor
                if y < height - 1 {
                    graph.set_neighbor_cap(node, 0, 1, 10)?;
                }
            }
        }
        
        // Use the generic function
        compute_and_analyze(&mut graph, "GridGraph2D4C (4-connected)");
        
        // Check segmentation
        let source_segment = graph.get_segment(source);
        let sink_segment = graph.get_segment(sink);
        println!("Source node segment: {:?}", source_segment);
        println!("Sink node segment: {:?}", sink_segment);
    }
    
    // Example 2: GridGraph2D8C (8-connected)
    {
        let mut graph = GridGraph2D8C::<i32, i32, i32>::new(width, height)?;
        
        // Set up the same problem
        let source = graph.node_id(0, 0)?;
        graph.set_terminal_cap(source, 100, 0)?;
        
        let sink = graph.node_id(width - 1, height - 1)?;
        graph.set_terminal_cap(sink, 0, 100)?;
        
        // Add neighbor capacities (including diagonals)
        for y in 0..height {
            for x in 0..width {
                let node = graph.node_id(x, y)?;
                
                // Orthogonal neighbors
                if x < width - 1 {
                    graph.set_neighbor_cap(node, 1, 0, 10)?;
                }
                if y < height - 1 {
                    graph.set_neighbor_cap(node, 0, 1, 10)?;
                }
                
                // Diagonal neighbors
                if x < width - 1 && y < height - 1 {
                    graph.set_neighbor_cap(node, 1, 1, 7)?; // Diagonal down-right
                }
            }
        }
        
        // Use the generic function
        compute_and_analyze(&mut graph, "GridGraph2D8C (8-connected)");
        
        let source_segment = graph.get_segment(source);
        let sink_segment = graph.get_segment(sink);
        println!("Source node segment: {:?}", source_segment);
        println!("Sink node segment: {:?}", sink_segment);
    }
    
    // Example 3: GridGraph2D4CMT (multi-threaded)
    {
        let mut graph = GridGraph2D4CMT::<i32, i32, i32>::new(width, height)?;
        
        // Set up the same problem
        let source = graph.node_id(0, 0)?;
        graph.set_terminal_cap(source, 100, 0)?;
        
        let sink = graph.node_id(width - 1, height - 1)?;
        graph.set_terminal_cap(sink, 0, 100)?;
        
        // Add neighbor capacities
        for y in 0..height {
            for x in 0..width {
                let node = graph.node_id(x, y)?;
                
                if x < width - 1 {
                    graph.set_neighbor_cap(node, 1, 0, 10)?;
                }
                if y < height - 1 {
                    graph.set_neighbor_cap(node, 0, 1, 10)?;
                }
            }
        }
        
        // Use the generic function
        compute_and_analyze(&mut graph, "GridGraph2D4CMT (multi-threaded)");
        
        let source_segment = graph.get_segment(source);
        let sink_segment = graph.get_segment(sink);
        println!("Source node segment: {:?}", source_segment);
        println!("Sink node segment: {:?}", sink_segment);
    }
    
    println!("\n=== Summary ===");
    println!("The GridGraph trait allows writing generic code that works");
    println!("with any grid graph implementation, enabling code reuse and");
    println!("flexibility in choosing the right graph type for your needs.");
    
    Ok(())
}
